﻿//---------------------------------------------------------------------------------
// Copyright 2011 Microsoft Corporation
// Licensed under the Microsoft Public License (MS-PL) (the "License"); 
// You may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.opensource.org/licenses/ms-pl.html
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR 
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, 
// INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR 
// CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
// MERCHANTABLITY OR NON-INFRINGEMENT. 
// See the Microsoft Public License (MS-PL) for the specific language governing 
// permissions and limitations under the License.
//---------------------------------------------------------------------------------

using Microsoft.Telco.Controls.WindowsPhone.Properties;
using Microsoft.Xna.Framework.Audio;
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace Microsoft.Telco.Controls.WindowsPhone
{
    /// <summary>
    /// The Transcribe Media control
    /// </summary>
  
    #region Parts and States Model Declarations

    [TemplatePart(Name = "RootElement", Type = typeof(FrameworkElement))]
    [TemplatePart(Name = "ControlBorder", Type = typeof(Border))]
    [TemplatePart(Name = "MediaGrid", Type = typeof(Grid))]
    [TemplatePart(Name = "ToolBorder", Type = typeof(Border))]
    [TemplatePart(Name = "RecordButton", Type = typeof(Button))]
    [TemplatePart(Name = "RecordEllipse", Type = typeof(Ellipse))]
    [TemplatePart(Name = "StopRecordButton", Type = typeof(Button))]
    [TemplatePart(Name = "StopRecordRectangle", Type = typeof(System.Windows.Shapes.Rectangle))]
    [TemplatePart(Name = "RecordingActivityGrid", Type = typeof(Grid))]
    [TemplatePart(Name = "RecordingActivityBorder", Type = typeof(Border))]
    [TemplatePart(Name = "RecordingActivityStackPanel", Type = typeof(StackPanel))]
    [TemplatePart(Name = "PlayButton", Type = typeof(Button))]
    [TemplatePart(Name = "PlayPath", Type = typeof(System.Windows.Shapes.Path))]
    [TemplatePart(Name = "StopPlayButton", Type = typeof(Button))]
    [TemplatePart(Name = "StopPlayRectangle", Type = typeof(System.Windows.Shapes.Rectangle))]
    [TemplatePart(Name = "StartTimeTextBlock", Type = typeof(TextBlock))]
    [TemplatePart(Name = "EndTimeTextBlock", Type = typeof(TextBlock))]
    [TemplatePart(Name = "PlaySlider", Type = typeof(Slider))]
    [TemplatePart(Name = "VolumeButton", Type = typeof(Button))]
    [TemplatePart(Name = "VolumePath", Type = typeof(System.Windows.Shapes.Path))]
    [TemplatePart(Name = "VolumeSlider", Type = typeof(Slider))]
    [TemplatePart(Name = "InputTextBox", Type = typeof(TextBox))]
    [TemplateVisualState(Name = "None", GroupName = "TransCribeMediaControlStates")]
    [TemplateVisualState(Name = "Playing", GroupName = "TransCribeMediaControlStates")]
    [TemplateVisualState(Name = "Recording", GroupName = "TransCribeMediaControlStates")]

    #endregion

    public class TranscribeMediaControl : TelcoControlBase
    {

        #region Private Fields/Objects

        private System.Windows.Threading.DispatcherTimer recordInProgressTimer;
        private System.Windows.Threading.DispatcherTimer durrationUpdateTimer;
        private System.Windows.Threading.DispatcherTimer playSliderUpdateTimer;
        private int tickCount=0;
        private int recordingProgressWidth = 10;
        private int maxRecordingProgressLength = 0;
        private int totalRectangles = 0;
        private double recordingProgressHeight = 0.0D;
        private double totalSliderDuration = 0.0D;
        private DateTime startTime;
        private DateTime endTime;
        private DateTime playTime;
        MemoryStream inputAudioStream;
        Microphone mic = Microphone.Default;
        SoundEffect soundEffect;
        SoundEffectInstance soundEffectInstance;
        private float soundEffectVloume = 0.5f;

        #endregion

        #region Parts

        private FrameworkElement RootElement { get; set; }
        private Border ControlBorderElement { get; set; }
        private Grid MediaGridElement { get; set; }
        private Border ToolBorderElement { get; set; }
        private Button RecordButtonElement { get; set; }
        private Ellipse RecordEllipseElement { get; set; }
        private Button StopRecordButtonElement { get; set; }
        private System.Windows.Shapes.Rectangle StopRecordRectangleElement { get; set; }
        private Grid RecordingActivityGridElement { get; set; }
        private Border RecordingActivityBorderElement { get; set; }
        private StackPanel RecordingActivityStackPanelElement { get; set; }
        private Button PlayButtonElement { get; set; }
        private System.Windows.Shapes.Path PlayPathElement { get; set; }
        private Button StopPlayButtonElement { get; set; }
        private System.Windows.Shapes.Rectangle StopPlayRectangleElement { get; set; }
        private TextBlock StartTimeTextBlockElement { get; set; }
        private TextBlock EndTimeTextBlockElement { get; set; }
        private Slider PlaySliderElement { get; set; }
        private Button VolumeButtonElement { get; set; }
        private System.Windows.Shapes.Path VolumePathElement { get; set; }
        private Slider VolumeSliderElement { get; set; }
        private TextBox InputTextBoxElement { get; set; }

        #endregion

        #region Events

        /// <summary>
        /// Occurs on starting of the Record
        /// </summary>
        public event EventHandler OnStartRecording;

        /// <summary>
        /// Occurs on starting of the Play
        /// </summary>
        public event EventHandler OnStartPlaying;

        /// <summary>
        /// Occurs on stopping the record
        /// </summary>
        public event EventHandler OnStopRecording;

        /// <summary>
        /// Occurs on stopping the play
        /// </summary>
        public event EventHandler OnStopPlaying;

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or Sets the MediaControl states  of the Transcibe Media
        /// </summary>
        protected MediaControlStates TrascribeMediaControlState { get; set; }

        /// <summary>
        /// Gets or Sets the Memory stream from audio source
        /// </summary>
        public MemoryStream InputAudioStream
        {
            get { return inputAudioStream; }
            set { inputAudioStream = value; }
        }

        ///// <summary>
        ///// Gets or Sets the WaveMediaSource to be played or recorded
        ///// </summary>
        //public WaveMediaStreamSource Media
        //{
        //    get { return wavMss; }
        //    set { wavMss = value; }
        //}

        /// <summary>
        /// Gets or sets text for conversion to and from speech
        /// </summary>
        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }
        
        /// <summary>
        /// The default text that to be displayed in the textbox while control is loaded
        /// </summary>
        public string DefaultInPutText
        {
            get;
            set;
        }

        #endregion

        #region Dependency Properties
        /// <summary>
        /// The TextProperty dependency property specifies the Text for conversion to and from speech
        /// </summary>
        public static readonly DependencyProperty TextProperty =
                    DependencyProperty.Register("Text", typeof(string), typeof(TranscribeMediaControl),
                    new PropertyMetadata(string.Empty, new PropertyChangedCallback(TextChangedCallback)));



        #endregion

        #region CallBack methods

        private static void TextChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            TranscribeMediaControl transcribeMediaControl = (TranscribeMediaControl)obj;
            transcribeMediaControl.SetText();
        }

        #endregion

        /// <summary>
        /// Constructor for TranscribeMediaControl
        /// </summary>
        public TranscribeMediaControl()
        {
            recordInProgressTimer = new System.Windows.Threading.DispatcherTimer();
            durrationUpdateTimer = new System.Windows.Threading.DispatcherTimer();
            playSliderUpdateTimer = new System.Windows.Threading.DispatcherTimer();
            mic.BufferDuration = TimeSpan.FromMilliseconds(300);
            mic.BufferReady += new EventHandler<EventArgs>(mic_BufferReady);
            SoundEffect.MasterVolume = 1.0f;
            DefaultInPutText = ControlResources.TranscribeMediadefaultInputText;
            DefaultStyleKey = typeof(TranscribeMediaControl);
        }

        #region Initialization

        /// <summary>
        /// Virtual method to be override on the base class
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            this.RootElement = base.GetTemplateChild("RootElement") as FrameworkElement;
            ControlBorderElement = GetTemplateChild("ControlBorder") as Border;
            MediaGridElement = GetTemplateChild("MediaGrid") as Grid;
            ToolBorderElement = GetTemplateChild("ToolBorder") as Border;
            RecordButtonElement = GetTemplateChild("RecordButton") as Button;
            RecordEllipseElement = GetTemplateChild("RecordEllipse") as Ellipse;
            StopRecordButtonElement = GetTemplateChild("StopRecordButton") as Button;
            StopRecordRectangleElement = GetTemplateChild("StopRecordRectangle") as System.Windows.Shapes.Rectangle;
            RecordingActivityGridElement = GetTemplateChild("RecordingActivityGrid") as Grid;
            RecordingActivityBorderElement = GetTemplateChild("RecordingActivityBorder") as Border;
            RecordingActivityStackPanelElement = GetTemplateChild("RecordingActivityStackPanel") as StackPanel;
            PlayButtonElement = GetTemplateChild("PlayButton") as Button;
            PlayPathElement = GetTemplateChild("PlayPath") as System.Windows.Shapes.Path;
            StopPlayButtonElement = GetTemplateChild("StopPlayButton") as Button;
            StopPlayRectangleElement = GetTemplateChild("StopPlayRectangle") as System.Windows.Shapes.Rectangle;
            StartTimeTextBlockElement = GetTemplateChild("StartTimeTextBlock") as TextBlock;
            EndTimeTextBlockElement = GetTemplateChild("EndTimeTextBlock") as TextBlock;
            PlaySliderElement = GetTemplateChild("PlaySlider") as Slider;
            VolumeButtonElement = GetTemplateChild("VolumeButton") as Button;
            VolumePathElement = GetTemplateChild("VolumePath") as System.Windows.Shapes.Path;
            VolumeSliderElement = GetTemplateChild("VolumeSlider") as Slider;
            InputTextBoxElement = GetTemplateChild("InputTextBox") as TextBox;;
            AddEventHandlersToElements();
            SetMediaButtonsContentSize();
            SetVolumeSliderMinMax();
            recordInProgressTimer.Interval = new TimeSpan(0, 0, 0, 0, 300);
            durrationUpdateTimer.Interval = new TimeSpan(0, 0, 0, 0, 1000);
            playSliderUpdateTimer.Interval = new TimeSpan(0, 0, 0, 100);
            if (InputTextBoxElement != null)
            {
                InputTextBoxElement.Text = DefaultInPutText;
            }
        }

        #endregion

        #region Methods

        private void SetText()
        {
            if (InputTextBoxElement != null)
            {
                InputTextBoxElement.Text = Text;
            }
        }

        /// <summary>
        /// Adds handler to the necessary controls
        /// </summary>
        private void AddEventHandlersToElements()
        {
            if (RecordButtonElement != null)
            {
                RecordButtonElement.Click += new RoutedEventHandler(RecordButtonElement_Click);
            }
            if (StopRecordButtonElement != null)
            {
                StopRecordButtonElement.Click += new RoutedEventHandler(StopRecordButtonElement_Click);
            }
            if (PlayButtonElement != null)
            {
                PlayButtonElement.Click += new RoutedEventHandler(PlayButtonElement_Click);
            }
            if (StopPlayButtonElement != null)
            {
                StopPlayButtonElement.Click += new RoutedEventHandler(StopPlayButtonElement_Click);
            }
            if (recordInProgressTimer != null)
            {
                recordInProgressTimer.Tick += new EventHandler(recordInProgressTimer_Tick);
            }
            if (durrationUpdateTimer != null)
            {
                durrationUpdateTimer.Tick += new EventHandler(durrationUpdateTimer_Tick);
            }
            if (playSliderUpdateTimer != null)
            {
                playSliderUpdateTimer.Tick += new EventHandler(playSliderUpdateTimer_Tick);
            }
            if (VolumeSliderElement != null)
            {
                VolumeSliderElement.ValueChanged += new RoutedPropertyChangedEventHandler<double>(VolumeSliderElement_ValueChanged);
            }
            if (InputTextBoxElement != null)
            {
                InputTextBoxElement.TextChanged += new TextChangedEventHandler(InputTextBoxElement_TextChanged);
            }
            if (InputTextBoxElement != null)
            {
                InputTextBoxElement.GotFocus += new RoutedEventHandler(InputTextBoxElement_GotFocus);
            }
            if (InputTextBoxElement != null)
            {
                InputTextBoxElement.LostFocus += new RoutedEventHandler(InputTextBoxElement_LostFocus);
            }
        }

        /// <summary>
        /// Method to set the Media button contents
        /// </summary>
        private void SetMediaButtonsContentSize()
        {
           
            double contentHeight = 15.0D;
            double contentWidth = 15.0D;
            double buttonHeight = 25.0D;
            double buttonWidth = 25.0D;
            if (!double.IsNaN(this.Height) || this.Height==0)
            {
                buttonHeight = .20 * Height;
                contentHeight = .12 * Height;
            }
            if (!double.IsNaN(this.Width) || this.Width == 0)
            {
                buttonWidth = .125 * Width;
                contentWidth = .075 * Width;
            }
            if (RecordButtonElement != null)
            {
                RecordButtonElement.Width = buttonWidth;
                RecordButtonElement.Height = buttonHeight;
            }
            if (RecordEllipseElement != null)
            {
                RecordEllipseElement.Height = contentHeight - (contentHeight * .2);
                RecordEllipseElement.Width = contentHeight - (contentHeight * .2);
            }
            if (PlayButtonElement != null)
            {
                PlayButtonElement.Width = buttonWidth;
                PlayButtonElement.Height = buttonHeight;
            }
            if (PlayPathElement != null)
            {
                PlayPathElement.Height = contentHeight;
                PlayPathElement.Width = contentWidth;
            }
            if (VolumeButtonElement != null)
            {
                VolumeButtonElement.Width = buttonWidth;
                VolumeButtonElement.Height = buttonHeight;
            }
            if (VolumePathElement != null)
            {
                VolumePathElement.Height = contentHeight-5;
                VolumePathElement.Width = contentWidth-2;
            }
            if(StopRecordButtonElement!=null)
            {
                StopRecordButtonElement.Width = buttonWidth;
                StopRecordButtonElement.Height = buttonHeight;
            }
            if (StopPlayButtonElement != null)
            {
                StopPlayButtonElement.Width = buttonWidth;
                StopPlayButtonElement.Height = buttonHeight;
            }
            if (StopRecordRectangleElement != null)
            {
                StopRecordRectangleElement.Height = contentHeight - (contentHeight * .3);
                StopRecordRectangleElement.Width = contentHeight - (contentHeight * .3);
            }
            if (StopPlayRectangleElement != null)
            {
                StopPlayRectangleElement.Height = contentHeight - (contentHeight * .3);
                StopPlayRectangleElement.Width = contentHeight - (contentHeight * .3);
            }
        }

        /// <summary>
        /// Method to control the recording progress animation
        /// </summary>
        private void SetRecordingProgressValues()
        {
            try
            {
                RecordingActivityStackPanelElement.Children.Clear();
                recordingProgressHeight = PlaySliderElement.ActualHeight;
                string[] splitValues = Math.Round(PlaySliderElement.ActualWidth).ToString().Split('.');
                maxRecordingProgressLength = int.Parse(splitValues[0]);
                totalRectangles = maxRecordingProgressLength / recordingProgressWidth;
                RecordingActivityStackPanelElement.Width = maxRecordingProgressLength;
                RecordingActivityStackPanelElement.Height = recordingProgressHeight / 2;
            }
            catch { }
        }

        /// <summary>
        /// Control visual state transition 
        /// </summary>
        /// <param name="useTransitions"></param>
        private void UpdateTranscribeMediaStates(bool useTransitions)
        {
            switch (TrascribeMediaControlState)
            {
                case MediaControlStates.None:
                    VisualStateManager.GoToState(this, "None", useTransitions);
                    break;
                case MediaControlStates.Playing:
                    VisualStateManager.GoToState(this, "Playing", useTransitions);
                    break;
                case MediaControlStates.Recording:
                    VisualStateManager.GoToState(this, "Recording", useTransitions);
                    break;
            }
        }

        /// <summary>
        /// Method to start the recording from mic
        /// </summary>
        private void StartMicRecording()
        {
            try
            {
                if (InputAudioStream != null)
                {
                    InputAudioStream.Close();                 
                }
                InputAudioStream = new MemoryStream(); 
                mic.Start();  
            }
            catch (Exception ex) { MessageBox.Show(ex.Message); }
         
        }

        /// <summary>
        /// Method to stop the mic recording
        /// </summary>
        private void StopMicRecording()
        {
            if (mic.State != MicrophoneState.Stopped)
                mic.Stop();
            //InputAudioStream.Position = 0;
            //SoundEffect se = new SoundEffect(inputAudioStream.ToArray(), mic.SampleRate, AudioChannels.Mono);
            //se.Play(1.0f, (float)slider1.Value, 0.0f);
        }

        /// <summary>
        /// Method to start play
        /// </summary>
        public void Play()
        {
            if (InputAudioStream != null)
            {
                InputAudioStream.Position = 0;
                soundEffect = new SoundEffect(inputAudioStream.ToArray(), mic.SampleRate, AudioChannels.Mono);
                soundEffectInstance = soundEffect.CreateInstance();
                playTime = System.DateTime.Now;
                totalSliderDuration = soundEffect.Duration.TotalSeconds;
                playSliderUpdateTimer.Interval = new TimeSpan(0, 0, 0, 0, 10);
                PlaySliderElement.Maximum = PlaySliderElement.ActualWidth;
                soundEffectInstance.Volume = soundEffectVloume;
                soundEffectInstance.Play();
                playSliderUpdateTimer.Start();
            }
        }

        /// <summary>
        /// Method to set the Volume slider minimum and maximum
        /// </summary>
        private void SetVolumeSliderMinMax()
        {
            VolumeSliderElement.Minimum = 0;
            VolumeSliderElement.Maximum = 1;
        }

        #endregion

        #region Event Handler

        void InputTextBoxElement_LostFocus(object sender, RoutedEventArgs e)
        {
            if (InputTextBoxElement.Text.Trim() == string.Empty)
            {
                InputTextBoxElement.Text = DefaultInPutText;
            }
        }

        void InputTextBoxElement_GotFocus(object sender, RoutedEventArgs e)
        {
            if (InputTextBoxElement.Text.Trim() == DefaultInPutText)
            {
                InputTextBoxElement.Text = string.Empty;
            }
        }

        void RecordButtonElement_Click(object sender, RoutedEventArgs e)
        {
            TrascribeMediaControlState = MediaControlStates.Recording;
            UpdateTranscribeMediaStates(true);
            SetRecordingProgressValues();
            StartTimeTextBlockElement.Text = System.DateTime.Now.ToShortTimeString().Remove(System.DateTime.Now.ToShortTimeString().Length - 2, 2);
            startTime = System.DateTime.Now;
            recordInProgressTimer.Start();
            durrationUpdateTimer.Start();
            if(OnStartRecording!=null)
            {
                OnStartRecording(this, new EventArgs());
            }
            StartMicRecording();
        }

        void StopRecordButtonElement_Click(object sender, RoutedEventArgs e)
        {
            StopMicRecording();
            recordInProgressTimer.Stop();
            durrationUpdateTimer.Stop();
            if (OnStopRecording != null)
            {
                OnStopRecording(this, new EventArgs());
            }
            TrascribeMediaControlState = MediaControlStates.None;
            UpdateTranscribeMediaStates(true);

        }

        void PlayButtonElement_Click(object sender, RoutedEventArgs e)
        {
            TrascribeMediaControlState = MediaControlStates.Playing;
            UpdateTranscribeMediaStates(true);
            if (OnStartPlaying != null)
            {
                OnStartPlaying(this, new EventArgs());
            }
        }

        void StopPlayButtonElement_Click(object sender, RoutedEventArgs e)
        {
            PlaySliderElement.Value = 0.0;
            playSliderUpdateTimer.Stop();
            TrascribeMediaControlState = MediaControlStates.None;
            if (OnStopPlaying != null)
            {
                OnStopPlaying(this, new EventArgs());
            }
            UpdateTranscribeMediaStates(true);
        }

        void playSliderUpdateTimer_Tick(object sender, EventArgs e)
        {
            PlaySliderElement.Value = (System.DateTime.Now.Subtract((playTime)).Seconds / totalSliderDuration) * PlaySliderElement.ActualWidth;
            EndTimeTextBlockElement.Text = System.DateTime.Now.Subtract(playTime).Hours.ToString() + " : " + System.DateTime.Now.Subtract(playTime).Minutes.ToString() + " : " + System.DateTime.Now.Subtract(playTime).Seconds.ToString();
            if (soundEffectInstance.State == SoundState.Stopped && PlaySliderElement.Value >= PlaySliderElement.Maximum)
            {
                playSliderUpdateTimer.Stop();
                PlaySliderElement.Value = 0.0;
                //PlaySliderElement.Value = (System.DateTime.Now.Subtract(playTime.Add(new TimeSpan(0,0,0,0,10))).Seconds / totalSliderDuration) * PlaySliderElement.ActualWidth;
                TrascribeMediaControlState = MediaControlStates.None;
                UpdateTranscribeMediaStates(true);
            }
        }

        void durrationUpdateTimer_Tick(object sender, EventArgs e)
        {
            endTime = System.DateTime.Now;
            TimeSpan duration = endTime.Subtract(startTime);
            EndTimeTextBlockElement.Text = duration.Hours.ToString() + " : " + duration.Minutes.ToString() + " : " + duration.Seconds.ToString();
        }

        void recordInProgressTimer_Tick(object sender, EventArgs e)
        {
            System.Windows.Shapes.Rectangle rectangle;
            if (tickCount == totalRectangles - 1)
            {
                tickCount = 0;
                RecordingActivityStackPanelElement.Children.Clear();
            }
            tickCount++;
            rectangle = new System.Windows.Shapes.Rectangle();
            rectangle.VerticalAlignment = VerticalAlignment.Center;
            rectangle.Height = recordingProgressHeight / 2.5;
            rectangle.Width = recordingProgressWidth;
            rectangle.Fill = new SolidColorBrush(Colors.Green);
            RecordingActivityStackPanelElement.Children.Add(rectangle);

        } 

        void TranscribeMediaElement_MediaEnded(object sender, RoutedEventArgs e)
        {
            playSliderUpdateTimer.Stop();
            //TranscribeMediaElement.ClearValue(MediaElement.SourceProperty);
            TrascribeMediaControlState = MediaControlStates.None;
            UpdateTranscribeMediaStates(true);
        }

        void VolumeSliderElement_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            try
            {
                soundEffectVloume = float.Parse(e.NewValue.ToString());
                SoundEffect.MasterVolume = float.Parse(e.NewValue.ToString());
            }
            catch { SoundEffect.MasterVolume = 1.0f; soundEffectVloume = 0.5f; }
            if (soundEffectInstance != null)
            {
                soundEffectInstance.Volume = soundEffectVloume;
            }
        }

        void InputTextBoxElement_TextChanged(object sender, TextChangedEventArgs e)
        {
            Text = InputTextBoxElement.Text;
        }

        void mic_BufferReady(object sender, EventArgs e)
        {
            byte[] buffer = new byte[1024];
            int bytesRead = 0;
            while ((bytesRead = mic.GetData(buffer, 0, buffer.Length)) > 0)
                InputAudioStream.Write(buffer, 0, bytesRead);
           
        }
                
        #endregion
    }
}
